﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RWLib;
using Action = RWLib.Action;

namespace RWTest
{
    [TestClass]
    public class UnitTest1
    {

        [TestMethod]
        public void Permutations() {
            List<Fluent> perm = new List<Fluent>() { new Fluent("a", true, false), new Fluent("b", false, true), new Fluent("c", true, true) };
            Assert.IsTrue(perm.Permutates().Count()==8);
            Formula f = new Formula(new List<Clause>(){
                new Clause(new List<Fluent>() { new Fluent("a",false,true)}),
                new Clause(new List<Fluent>() { new Fluent("b",false,false)}),
                new Clause(new List<Fluent>() { new Fluent("c",false,true)})
            });
            List<List<Fluent>> actual = f.Fluents.Permutates().Where(l => f.Eval(l)).ToList();
            Assert.IsTrue(actual.Count() == 1);
        }

        [TestMethod]
        public void Conflicts() {
            List<Fluent> fluents = new List<Fluent>() { new Fluent("a", false, false), new Fluent("b", false, false), new Fluent("a", false, true), new Fluent("b", false, false)
            , new Fluent("e", false, true), new Fluent("f", false, false)};
            List<Clause> clause1 = new List<Clause>() { new Clause(new List<Fluent>(){fluents[0]}), new Clause(new List<Fluent>(){fluents[1]}) };
            List<Clause> clause2 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[2] }), new Clause(new List<Fluent>() { fluents[3] }) };
            List<Clause> clause3 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[4] }), new Clause(new List<Fluent>() { fluents[5] }) };
            List<Clause> clause4 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[0] }), new Clause(new List<Fluent>() { fluents[1] }) };
            Formula f1 = new Formula(clause1);
            Formula f2 = new Formula(clause2);
            Formula f3 = new Formula(clause3);
            Formula f4 = new Formula(clause4);
            Assert.IsTrue(f1.Conflicts(f2));
            Assert.IsFalse(f1.Conflicts(f3));
            var A = new FluentEffectStatement("A",new Formula(),f1 );
            var B = new FluentEffectStatement("A", new Formula(), f2);
            var D = new FluentEffectStatement("D", new Formula(), f3);
            var C = new FluentEffectStatement("C", new Formula(), f4);
            List<Fluent> state = new List<Fluent>() { new Fluent("a", true, false), new Fluent("b", true, false) };
            List<Fluent> state1 = new List<Fluent>() { new Fluent("a", true, false), new Fluent("b", true, false),
            new Fluent("e", true, false), new Fluent("f", false, false)};
            Assert.IsTrue(A.Conflict(B, state));
            Assert.IsTrue(B.Conflict(A, state));
            Assert.IsFalse(A.Conflict(C, state));
            Assert.IsTrue(B.Conflict(C, state));

            Assert.IsFalse(A.Conflict(D, state1));
            Assert.IsFalse(B.Conflict(D, state1));
            Assert.IsFalse(C.Conflict(D, state1));       
            
        }

        [TestMethod]
        public void ResolveConflicts() {
            List<Fluent> fluents = new List<Fluent>() { new Fluent("a", false, false), new Fluent("b", false, false), new Fluent("a", false, true), new Fluent("b", false, false)
            , new Fluent("e", false, true), new Fluent("f", false, false)};
            List<Clause> clause1 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[0] }), new Clause(new List<Fluent>() { fluents[1] }) };
            List<Clause> clause2 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[2] }), new Clause(new List<Fluent>() { fluents[3] }) };
            List<Clause> clause3 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[4] }), new Clause(new List<Fluent>() { fluents[5] }) };
            List<Clause> clause4 = new List<Clause>() { new Clause(new List<Fluent>() { fluents[0] }), new Clause(new List<Fluent>() { fluents[1] }) };
            Formula f1 = new Formula(clause1);
            Formula f2 = new Formula(clause2);
            Formula f3 = new Formula(clause3);
            Formula f4 = new Formula(clause4);
            Assert.IsTrue(f1.Conflicts(f2));
            Assert.IsFalse(f1.Conflicts(f3));
            var A = new FluentEffectStatement("A", new Formula(), f1);
            var B = new FluentEffectStatement("B", new Formula(), f2);
            var B1 = new FluentEffectStatement("B1", new Formula(), f2);
            var B2 = new FluentEffectStatement("B2", new Formula(), f2);
            var D = new FluentEffectStatement("D", new Formula(), f3);
            var C = new FluentEffectStatement("C", new Formula(), f4);
            List<Fluent> state = new List<Fluent>() { new Fluent("a", true, false), new Fluent("b", true, false), new Fluent("e", false, true), new Fluent("f", false, false) };
            List<Fluent> state1 = new List<Fluent>() { new Fluent("a", true, false), new Fluent("b", true, false),
            new Fluent("e", true, false), new Fluent("f", false, false)};

            List<StatementBase> actions = new List<StatementBase>() { A, C, C,C,C,A,A,B, B1, B2, B, B1, B2 };
            Assert.IsTrue(B.Conflict(C, state));
            var actual = actions.ResolveConflicts(state);
            Assert.IsTrue(actions.ResolveConflicts(state).Count==2);
            var expected = new List<List<StatementBase>>(){
                new List<StatementBase>(){A, C, C,C,C,A,A},
                new List<StatementBase>(){B, B1, B2, B, B1, B2}
            };
            for (int i = 0; i < actual.Count(); ++i) {
                Assert.IsTrue(Enumerable.SequenceEqual(actual[i].OrderBy(t => t), expected[i].OrderBy(t => t)));
            }                
        }

        [TestMethod]
        public void Example1()
        {
            var text = "initially not dead and not loaded and alive\n" +
                       "Load releases loaded\n" +
                       "Shoot causes dead";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "Load", "Shoot" });     
            Tree tree = parser.BuildTree(scenario);
            Assert.IsTrue(tree.Root.Nodes.First().Nodes.Any(n => n.Fluents.Any(f => f.Name == "loaded" && f.Value == true)) &&
                tree.Root.Nodes.First().Nodes.Any(n => n.Fluents.Any(f => f.Name == "loaded" && f.Value == false)) &&
                tree.Root.Nodes.First().Nodes.All(n => n.actions.Any(a => a.Name == "Shoot") && n.Fluents.Any(f => f.Name == "dead" && f.Value == true)));
        }

        [TestMethod]
        public void Example2()
        {
            var text = "initially not dead and not loaded\n" +
                       "Load releases loaded\n" +
                       "Shoot causes dead if loaded";  
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "Load" });
            scenario.Add(2, new List<string> { "Shoot" });
            Tree tree = parser.BuildTree(scenario);
            Assert.IsTrue(parser.ParseQuery(tree, "dead sometimes true at 3 during sc"));
        }

        [TestMethod]
        public void Example3()
        {
            var text = "initially not right and not left\n" +
                       "goright causes right if not left\n" +
                       "goleft causes left if not right";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "goleft", "goright" });
            Tree tree = parser.BuildTree(scenario);            
            Assert.IsTrue(tree.Root.Nodes.First().Nodes.Any(n => n.Fluents.Any(f => f.Name == "left" && f.Value == true)) &&
                tree.Root.Nodes.First().Nodes.Any(n => n.Fluents.Any(f => f.Name == "right" && f.Value == true)) &&
                tree.Root.Nodes.First().Nodes.All(n => n.actions.Any(a => a.Name == "goleft") && n.actions.Any(a => a.Name == "goright")));
        }

        [TestMethod]
        public void Example4()
        {
            var text = "initially alive and not loadedL and not loadedR and not hidden\n" +
                       "LoadR causes loadedR\n" +
                       "LoadL causes loadedL\n" +
                       "Shoot_Fred_Right causes not alive if (not hidden and loadedR)\n" +
                       "Shoot_Fred_Left releases alive if (alive and not hidden and loadedL)\n" +
                       "Sing causes hidden if not hidden\n" +
                       "hidden triggers Wait\n" +
                       "Wait invokes Unhide after 3\n" +
                       "Unhide causes not hidden";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "LoadL", "LoadR" });
            scenario.Add(2, new List<string> { "Sing" });
            scenario.Add(8, new List<string> { "Shoot_Fred_Right" });
            Tree tree = parser.BuildTree(scenario);
            Assert.IsTrue(parser.ParseQuery(tree, "not alive always reachable after sc"));
        }

        [TestMethod]
        public void Example5()
        {
            var text = "initially thirsty and hungry and not clean and not dressed and not working and not boiled\n" +
                       "GoToWork causes working if (not thirsty and not hungry and clean and dressed)\n" +
                       "Eat causes not hungry\n" +
                       "TurnKettleOn invokes Boil after 4\n" +
                       "Boil causes boiled\n" +
                       "DrinkTea causes not thirsty\n" +
                       "impossible DrinkTea if not boiled\n" +
                       "TakeShower causes not dressed and clean if not hungry\n" +
                       "DressUp causes dressed";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "TurnKettleOn", "Eat" });
            scenario.Add(2, new List<string> { "TakeShower" });
            scenario.Add(3, new List<string> { "DressUp" });
            scenario.Add(5, new List<string> { "DrinkTea" });  
            scenario.Add(7, new List<string> { "GoToWork" });
            Tree tree = parser.BuildTree(scenario);
            Assert.IsTrue(parser.ParseQuery(tree, "working always reachable after sc"));
        }

        [TestMethod]
        public void Example6()
        {
            var text = "initially a and b\n" +
                       "A releases a\n" +
                       "B releases b";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "A", "B" });
            Tree tree = parser.BuildTree(scenario);
            try
            {
                Assert.IsFalse(parser.ParseQuery(tree, "a always reachable after sc"));
            }
            catch (FluentNotFoundException e)
            {
            }
        }

        [TestMethod]
        public void Example7()
        {
            var text = "initially not a and not b\n" +
                       "A releases a\n" +
                       "(not a or a) triggers A\n" +
                       "B causes b";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "A"});
            scenario.Add(6, new List<string> { "B" });
            Tree tree = parser.BuildTree(scenario);
            try
            {
                Assert.IsFalse(parser.ParseQuery(tree, "a always reachable after sc"));
            }
            catch (FluentNotFoundException e)
            {
            }
        }

        [TestMethod]
        public void Example8()
        {
            var text = "initially not dead and loaded\n" +
                       "Load releases loaded\n" +
                       "Shoot causes dead and not loaded if loaded";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "Load", "Shoot" });
            Tree tree = parser.BuildTree(scenario);
            try
            {
                Assert.IsFalse(parser.ParseQuery(tree, "always executable sc"));
            }
            catch (FluentNotFoundException e)
            {
            }
        }

        [TestMethod]
        public void Example9()
        {
            //var program = "initially not a\n" +
            //    "impossible akcja if not a\n" +
            //    "akcja causes a if not a";
            var text = "initially not a\n" +
                      "akcja causes a if not a\n" +
                      "impossible akcja if not a";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "akcja" });
            Tree tree = parser.BuildTree(scenario);

            Assert.IsFalse(parser.ParseQuery(tree, "always executable sc"));
            Assert.IsFalse(parser.ParseQuery(tree, "sometimes executable sc"));
        }

    }

}